/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.core.commands;


import java.util.Iterator;

import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.gef.commands.Command;
import org.eclipse.uml2.uml.ActivityEdge;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.Connector;
import org.eclipse.uml2.uml.ConnectorEnd;
import org.eclipse.uml2.uml.Dependency;
import org.eclipse.uml2.uml.DeployedArtifact;
import org.eclipse.uml2.uml.Deployment;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.ElementImport;
import org.eclipse.uml2.uml.Extend;
import org.eclipse.uml2.uml.Extension;
import org.eclipse.uml2.uml.Generalization;
import org.eclipse.uml2.uml.Include;
import org.eclipse.uml2.uml.Manifestation;
import org.eclipse.uml2.uml.PackageImport;
import org.eclipse.uml2.uml.ProfileApplication;
import org.eclipse.uml2.uml.Relationship;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.uml2.uml.TemplateBinding;
import org.eclipse.uml2.uml.Transition;

import com.cea.papyrus.core.commands.factories.DiagramElementDeleteCommandFactory;
import com.cea.papyrus.core.helper.EdgeInstanceSpecificationHelper;
import com.cea.papyrus.core.helper.GraphEdgeHelper;
import com.cea.papyrus.core.helper.IGraphElementHelper;
import com.cea.papyrus.core.helper.IModelerHelper;
import com.cea.papyrus.core.helper.ModelerHelper;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphEdge;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;

/**
 * Command that adds a grpah element corresponding to a given UML element to the current graph element.
 */
public class AddUMLElementToDiagram extends Command {

	/**
	 * The imodeler factory.
	 */
	protected IModelerHelper imodelerFactory=null;

	/**
	 * The element class.
	 */
	protected Class elementClass=null;

	/**
	 * Factory used to create the element.
	 */
	protected Object elementFactory;

	/**
	 * debug channel.
	 */
	protected final static int channel = 17;

	protected Command deletecommand;

	/**
	 * UML element added to the diagram.
	 */
	Element umlElement=null;

	/**
	 * graph element parent of the graph element to create.
	 */
	GraphElement parentGE;

	/**
	 * Creation location.
	 */
	Point location;

	/**
	 * Size of the graph element to create and add.
	 */
	protected Dimension size;

	/**
	 * Graph element added.
	 */
	protected GraphElement childGE;

	/**
	 * Creates a new {@link AddUMLElementToDiagram} command.
	 * 
	 * @param element the uml element represented by the graph element to create
	 * @param size the size of the created graph element
	 * @param location the location to create the new graph element (absolute coordinate)
	 * @param parentGE the parent graph element for the created graph node
	 */
	public AddUMLElementToDiagram(Element element, GraphElement parentGE, Point location, Dimension size){
		this.umlElement=element;
		if(element instanceof ElementImport){
			this.elementClass=((ElementImport)element).getImportedElement().getClass().getInterfaces()[0];
		}
		else{
			this.elementClass= this.umlElement.getClass().getInterfaces()[0];}

		this.imodelerFactory= ModelerHelper.getFactory();
		this.elementFactory = this.imodelerFactory.getElementFactory(this.elementClass);
		this.parentGE=parentGE;

		if (location!=null){
			this.location= location;
		}
		else{this.location= new Point(30,30);}
		if (this.size!=null){
			this.size=size;}
		else{this.size= new Dimension(100,100);}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.commands.Command#canExecute()
	 */
	/**
	 * 
	 * 
	 * @return 
	 */
	@Override
	public boolean canExecute() {
		if(elementFactory != null) {
			return ((IGraphElementHelper)elementFactory).canCreateGraphElement(parentGE);
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.commands.Command#execute()
	 */
	/**
	 * 
	 */
	@Override
	public void execute(){
		childGE=((IGraphElementHelper)elementFactory).createGraphElement(umlElement, parentGE, location, size);
		if(!(umlElement instanceof ElementImport)&&
				(umlElement instanceof Relationship||
						umlElement instanceof Connector||
						elementFactory instanceof EdgeInstanceSpecificationHelper||
						umlElement instanceof ActivityEdge||
						umlElement instanceof Transition)) {
			// TODO treat this section with factories in order to avoid a switch with all relationship kinds
			Element umlSource = null;
			Element umlTarget = null;

			if(umlElement instanceof ProfileApplication) {
				umlSource = ((ProfileApplication)umlElement).getApplyingPackage();
				umlTarget = ((ProfileApplication)umlElement).getAppliedProfile();
			}
			
			if (umlElement instanceof TemplateBinding) {
				umlSource = ((TemplateBinding)umlElement).getBoundElement() ;
				umlTarget = ((TemplateBinding)umlElement).getSignature().getTemplate() ;
			}
			else if(umlElement instanceof Connector) {				
				umlSource =((ConnectorEnd) ((Connector)umlElement).getEnds().get(0)).getRole();
				umlTarget =((ConnectorEnd) ((Connector)umlElement).getEnds().get(1)).getRole();

			}

			else if(umlElement instanceof PackageImport) {				
				umlSource = ((PackageImport)umlElement).getImportingNamespace();
				umlTarget = ((PackageImport)umlElement).getImportedPackage();
			}

			else if(umlElement instanceof Include) {
				umlSource = (Element)((Include)umlElement).getIncludingCase();
				umlTarget = (Element)((Include)umlElement).getAddition();
			}

			else if(umlElement instanceof Extend) {
				umlSource = (Element)((Extend)umlElement).getExtension();
				umlTarget = (Element)((Extend)umlElement).getExtendedCase();
			}

			else if(umlElement instanceof Deployment) {
				umlSource = (Element)((Deployment)umlElement).getLocation();

				Deployment de=(Deployment)umlElement;
				umlSource = de.getLocation();
				Iterator it = de.getDeployedArtifacts().iterator();
				int i = 1;
				while (it.hasNext()){
					if (i > 1) {
						childGE=((IGraphElementHelper)elementFactory).createGraphElement(umlElement, parentGE, location, size);

					}
					i++;
					DeployedArtifact deployedArtifact = (DeployedArtifact)it.next();
					umlTarget = deployedArtifact;
					((GraphEdgeHelper)elementFactory).endCreateGraphElement(umlSource, umlTarget, (GraphEdge)childGE, LookForElement.getDiagram(parentGE));
				}

				return;			
			}

			else if(umlElement instanceof Manifestation) {
				umlSource = (Element)((Manifestation)umlElement).getOwner();
				umlTarget = (Element)((Manifestation)umlElement).getUtilizedElement();
			}

			else if(umlElement instanceof Dependency) {
				umlSource = (Element)((Dependency)umlElement).getSources().get(0);;
				umlTarget = (Element)((Dependency)umlElement).getSuppliers().get(0);
			}

			else if(umlElement instanceof Extension) {
				// Affect source and target
				if (((Extension)umlElement).getEndTypes().get(0) instanceof Stereotype) {
					umlSource   = (Element) ((Extension)umlElement).getEndTypes().get(0);
					umlTarget = (Element) ((Extension)umlElement).getEndTypes().get(1);
				} else {
					umlSource   = (Element) ((Extension)umlElement).getEndTypes().get(1);
					umlTarget	= (Element) ((Extension)umlElement).getEndTypes().get(0);
				}
			}

			else if(umlElement instanceof Generalization) {
				umlSource = ((Generalization)umlElement).getSpecific();
				umlTarget = ((Generalization)umlElement).getGeneral();
			}

			else if(umlElement instanceof Association) {
				// Bug 239: if there is only one type in getEndTypes(), the association is an association from one element to itself
				final int size = ((Association)umlElement).getEndTypes().size();
				umlSource = (Element)((Association)umlElement).getEndTypes().get(0);
				if(size == 1) {
					umlTarget = umlSource;
				} else {
					umlTarget = (Element)((Association)umlElement).getEndTypes().get(1);
				}
			}

			else if(umlElement instanceof ActivityEdge) {
				umlSource = (Element)((ActivityEdge)umlElement).getSource();
				umlTarget = (Element)((ActivityEdge)umlElement).getTarget();
			}
			else if(umlElement instanceof Transition){
				umlSource = (Element)((Transition)umlElement).getSource();
				umlTarget = (Element)((Transition)umlElement).getTarget();
			}

			((GraphEdgeHelper)elementFactory).endCreateGraphElement(umlSource, umlTarget, (GraphEdge)childGE, LookForElement.getDiagram(parentGE));

			
		}
		deletecommand =DiagramElementDeleteCommandFactory.eINSTANCE.createCommand(childGE);
	}


	/* (non-Javadoc)
	 * @see org.eclipse.gef.commands.Command#undo()
	 */
	/**
	 * 
	 */
	@Override
	public void undo(){
		//((IGraphElementHelper)elementFactory).removeGraphElement(childGE,parentGE);
		deletecommand.execute();
	}

	/**
	 * 
	 * 
	 * @return 
	 */
	public boolean canUndo(){
		return deletecommand.canExecute();	

	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.commands.Command#redo()
	 */
	/**
	 * 
	 */
	@Override
	public void redo(){
		deletecommand.undo();
	}

}
